package com.zzl.alligator.management.vote.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import com.zzl.alligator.common.constant.SystemConstants;
import com.zzl.alligator.common.constant.VoteConstants;
import com.zzl.alligator.common.util.JobUtils;
import com.zzl.alligator.common.util.SpringUtils;
import com.zzl.alligator.framework.async.AsyncManager;
import com.zzl.alligator.framework.exception.CustomException;
import com.zzl.alligator.framework.job.VoteJob;
import com.zzl.alligator.management.monitor.domain.QrtzJobDetails;
import com.zzl.alligator.management.monitor.service.IQrtzJobDetailsService;
import com.zzl.alligator.management.system.domain.SysNotice;
import com.zzl.alligator.management.system.service.ISysNoticeService;
import com.zzl.alligator.management.system.service.ISysUserService;
import com.zzl.alligator.management.system.service.impl.SysNoticeService;
import com.zzl.alligator.management.vote.domain.VoteOption;
import com.zzl.alligator.management.vote.domain.VotePrincipal;
import com.zzl.alligator.management.vote.domain.VoteSubject;
import com.zzl.alligator.management.vote.domain.VoteUserSubject;
import com.zzl.alligator.management.vote.domain.VoteViewUser;
import com.zzl.alligator.management.vote.mapper.VoteOptionMapper;
import com.zzl.alligator.management.vote.mapper.VotePrincipalMapper;
import com.zzl.alligator.management.vote.mapper.VoteSubjectMapper;
import com.zzl.alligator.management.vote.mapper.VoteUserSubjectMapper;
import com.zzl.alligator.management.vote.mapper.VoteViewUserMapper;
import com.zzl.alligator.management.vote.service.IVoteSubjectService;
import com.zzl.alligator.management.vote.vo.OptionVO;
import com.zzl.alligator.management.vote.vo.PrincipalVO;
import com.zzl.alligator.management.vote.vo.SubjectVO;
import com.zzl.alligator.management.vote.vo.VoteGuideVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
import java.util.stream.Collectors;

/**
 * 投票主题(VoteSubject)服务层
 *
 * @author zhouziliang
 * @since 2020-07-22
 */
@Service
@Slf4j
public class VoteSubjectService implements IVoteSubjectService {
    
    @Autowired
    private VoteSubjectMapper voteSubjectMapper;
    @Autowired
    private VoteOptionMapper voteOptionMapper;
    @Autowired
    private VotePrincipalMapper votePrincipalMapper;
    @Autowired
    private VoteUserSubjectMapper voteUserSubjectMapper;
    @Autowired
    private VoteViewUserMapper voteViewUserMapper;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private IQrtzJobDetailsService jobDetailsService;
    /**
     * 开启任务前缀
     */
    private static final String START_VOTE_JOB = "START_VOTE_JOB_";
    /**
     * 结束任务前缀
     */
    private static final String CUTOFF_VOTE_JOB = "CUTOFF_VOTE_JOB_";

    /**
     * 通过ID查询单条数据
     *
     * @param subjectId 主键
     * @return 实例对象
     */
    @Override
    public VoteSubject findVoteSubjectById(Long subjectId) {
        return voteSubjectMapper.findVoteSubjectById(subjectId);
    }

    /**
     * 查找 vote
     *
     * @param subjectId 主题ID
     * @return 结果
     */
    @Override
    public VoteGuideVO findVoteSubjectGuideById(Long subjectId) {
        VoteGuideVO guideVO = new VoteGuideVO();
        // 1. 主题
        VoteSubject subject = voteSubjectMapper.findVoteSubjectById(subjectId);
        SubjectVO subjectVO = BeanUtil.copyProperties(subject, SubjectVO.class);
        subjectVO.setAnonymous(VoteConstants.ANONYMOUS_YES.equals(subject.getAnonymous()));
        subjectVO.setRandomOrder(VoteConstants.RANDOM_ORDER_YES.equals(subject.getRandomOrder()));
        subjectVO.setRequired(VoteConstants.REQUIRED_YES.equals(subject.getRequired()));
        guideVO.setSubject(subjectVO);

        // 2. 主体
        List<VotePrincipal> principalList = votePrincipalMapper.findVotePrincipalBySubjectId(subjectId);
        if(CollectionUtil.isNotEmpty(principalList)) {
            List<PrincipalVO> principalVOList = principalList.stream().map(principal -> {
                PrincipalVO principalVO = new PrincipalVO();
                principalVO.setDescription(principal.getDescription());
                return principalVO;
            }).collect(Collectors.toList());
            guideVO.setPrincipalList(principalVOList);
        }

        // 3. 选项
        List<VoteOption> voteOptionList = voteOptionMapper.findVoteOptionList(new VoteOption() {{
            setSubjectId(subjectId);
        }});
        if(CollectionUtil.isNotEmpty(voteOptionList)) {
            List<OptionVO> optionVOList = voteOptionList.stream().map(option -> {
                OptionVO optionVO = BeanUtil.copyProperties(option, OptionVO.class);
                optionVO.setDefaultOption(strToBool(option.getDefaultOption()));
                return optionVO;
            }).collect(Collectors.toList());
            guideVO.setOptionList(optionVOList);
        }

        // 4. 投票人
        List<Long> userIds = this.voteUsers(subjectId);
        if (CollectionUtil.isNotEmpty(userIds)) {
            guideVO.setVoteUserIds(userIds);
            guideVO.setVoteDeptIds(sysUserService.getDeptIdsByUserIds(userIds));
        }
        // 5.可见人
        List<Long> viewUsers = this.viewUsers(subjectId);
        if (CollectionUtil.isNotEmpty(viewUsers)) {
            guideVO.setViewUserIds(viewUsers);
            guideVO.setViewDeptIds(sysUserService.getDeptIdsByUserIds(viewUsers));
        }

        if (CollectionUtils.isEqualCollection(guideVO.getViewUserIds(), guideVO.getVoteUserIds())) {
            guideVO.setViewScope(true);
        } else {
            guideVO.setViewScope(false);
        }
        return guideVO;

    }

    private List<Long> voteUsers(Long subjectId) {
        List<VoteUserSubject> voteUserSubjectList =
                voteUserSubjectMapper.findVoteUserSubjectList(new VoteUserSubject() {{
                    setSubjectId(subjectId);
                }});
        if (CollectionUtil.isNotEmpty(voteUserSubjectList)) {
            List<Long> userIds =
                    voteUserSubjectList.stream().map(VoteUserSubject::getUserId).collect(Collectors.toList());
            return userIds;
        }

        return null;
    }
    private List<Long> viewUsers(Long subjectId) {
        List<VoteViewUser> voteViewUserList = voteViewUserMapper.findVoteViewUserList(new VoteViewUser() {{
            setSubjectId(subjectId);
        }});
        if(CollectionUtils.isNotEmpty(voteViewUserList)) {
            List<Long> userIds =
                    voteViewUserList.stream().map(VoteViewUser::getUserId).collect(Collectors.toList());
            return userIds;
        }
        return null;
    }

    /**
     * 是否
     *
     * @param str 字符串
     * @return   j
     */
    private Boolean strToBool(String str) {
        if (str == null) {
            return false;
        }
        return SystemConstants.YES.equals(str);
    }

    /**
     * 通过实体作为筛选条件查询
     *
     * @param voteSubject 实例对象
     * @return 对象列表
     */
    @Override
    public List<VoteSubject> findVoteSubjectList(VoteSubject voteSubject) {
        return voteSubjectMapper.findVoteSubjectList(voteSubject);
    }

    /**
     * 新增数据
     *
     * @param voteSubject 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertVoteSubject(VoteSubject voteSubject) {
        return voteSubjectMapper.insertVoteSubject(voteSubject);
    }

    /**
     * 修改数据
     *
     * @param voteSubject 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateVoteSubject(VoteSubject voteSubject) {
        return voteSubjectMapper.updateVoteSubject(voteSubject);
    }

    /**
     * 通过主键删除数据
     *
     * @param subjectId 主键
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteById(Long subjectId) {
        return voteSubjectMapper.deleteById(subjectId);
    }

    /**
     * 批量删除数据
     *
     * @param idList
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByIds(List<Long> idList) {
        idList.forEach(id -> {
            VoteSubject voteSubject = this.findVoteSubjectById(id);
            if (VoteConstants.STATUS_SUBJECT_STARTED.compareTo(voteSubject.getStatus()) <= 0) {
                // 如果状态为已开始以上， 则返回错误
                throw new CustomException("只可以删除未开始的投票");
            }
        });
        // 1.删除主体
        deletePrincipalBySubjectIds(idList);
        // 2.删除选项
        deleteOptionBySubjectIds(idList);
        // 3. 删除投票人
        deleteVoterBySubjectIds(idList);
        // 4.删除查看人
        deleteViewerBySubjectIds(idList);
        // 5.删除主题
        return voteSubjectMapper.deleteByIds(idList);

    }

    /**
     * 根据主题id 删除可见人
     *
     * @param idList 主题IDs
     * @return 条数
     */
    private int deleteViewerBySubjectIds(List<Long> idList) {
        return voteViewUserMapper.deleteViewerBySubjectIds(idList);
    }

    /**
     * 删除投票人
     * @param idList 主题ID
     * @return 条数
     */
    private int deleteVoterBySubjectIds(List<Long> idList) {
        return voteUserSubjectMapper.deleteVoterBySubjectIds(idList);
    }

    /**
     * 删除选项
     *
     * @param idList 主题ID
     * @return 条数
     */
    private int deleteOptionBySubjectIds(List<Long> idList) {
        return voteOptionMapper.deleteOptionBySubjectIds(idList);
    }

    /**
     * 根据主题ID删除主体
     *
     * @param idList id列表
     * @return 条数
     */
    private int deletePrincipalBySubjectIds(List<Long> idList) {
        return votePrincipalMapper.deletePrincipalBySubjectIds(idList);
    }

    /**
     * 根据投票向导 插入投票数据
     *
     * @param voteGuideVO 投票数据
     * @return 插入条数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertVote(VoteGuideVO voteGuideVO) {
        // 1. 主题
        VoteSubject subject = new VoteSubject();
        SubjectVO subVO = voteGuideVO.getSubject();
        BeanUtil.copyProperties(subVO, subject);
        LocalDateTime startTime = subject.getStartTime();
        LocalDateTime now = LocalDateTime.now();
        if(startTime != null) {
            // 开始时间是否在当前时间一分钟之后
            if (startTime.isBefore(now.plusMinutes(1))) {
                //直接置为开始
                subject.setStatus(VoteConstants.STATUS_SUBJECT_STARTED);
            } else {
                subject.setStatus(VoteConstants.STATUS_SUBJECT_NOTSTART);
            }
        } else {
            subject.setStatus(VoteConstants.STATUS_SUBJECT_STARTED);
        }

        subject.setRandomOrder(subVO.getRandomOrder() ? VoteConstants.RANDOM_ORDER_YES : VoteConstants.RANDOM_ORDER_NO);
        subject.setAnonymous(subVO.getAnonymous() ? VoteConstants.ANONYMOUS_YES : VoteConstants.ANONYMOUS_NO);
        subject.setRequired(subVO.getRequired() ? VoteConstants.REQUIRED_YES : VoteConstants.REQUIRED_NO);
        if(subVO.getRequired() != null) {
            subject.setRequired(subVO.getRequired() ? VoteConstants.REQUIRED_YES : VoteConstants.REQUIRED_NO);
        }
        int res = 0;
        if(subject.getSubjectId() != null) {
            List<Long> ids = ListUtil.toList(subject.getSubjectId());
            res = voteSubjectMapper.updateVoteSubject(subject);
            deleteOptionBySubjectIds(ids);
            deletePrincipalBySubjectIds(ids);
            deleteViewerBySubjectIds(ids);
            deleteVoterBySubjectIds(ids);
        }else {
            res = voteSubjectMapper.insertVoteSubject(subject);
        }
        final Long subjectId = subject.getSubjectId();
        // 2. 主体
        insertPrincipal(voteGuideVO, subjectId);
        // 3. 选项
        insertOption(voteGuideVO, subjectId);
        // 4. 投票人
        insertVoteUser(voteGuideVO, subjectId);
        // 5. 结果可见人
        insertViewUser(voteGuideVO, subjectId);
        //6. 如果投票状态为未开始，则开启定时器
        if (VoteConstants.STATUS_SUBJECT_NOTSTART.equals(subject.getSubjectId())) {
            startTask(subject);
        }
        // 7. 如果为非必须投票任务，则开启结束任务
        if(VoteConstants.REQUIRED_NO.equals(subject.getStatus())) {
            cutOffTask(subject);
        }
        return res;
    }


    /**
     * 插入可见人列表
     *
     * @param voteGuideVO vo
     * @param subjectId id
     * @return 条数
     */
    private int insertViewUser(VoteGuideVO voteGuideVO, Long subjectId) {
        List<Long> viewUserIds = voteGuideVO.getViewUserIds();
        if(viewUserIds == null || viewUserIds.size() == 0) {
            return 0;
        }
        List<VoteViewUser> vvu = viewUserIds.stream().distinct().map(id -> {
            VoteViewUser voteViewUser = new VoteViewUser();
            voteViewUser.setSubjectId(subjectId);
            voteViewUser.setUserId(id);
            return voteViewUser;
        }).collect(Collectors.toList());
        return voteViewUserMapper.batchInsert(vvu);
    }

    /**
     * 插入投票人
     *
     * @param voteGuideVO vo
     * @param subjectId id
     * @return 结果
     */
    private int insertVoteUser(VoteGuideVO voteGuideVO, Long subjectId) {
        List<Long> voteUserIds = voteGuideVO.getVoteUserIds();
        if(voteUserIds == null || voteUserIds.size() == 0) {
            return 0;
        }
        List<VoteUserSubject> userList = voteUserIds.stream()
                .distinct()
                .map(id -> {
            VoteUserSubject vus = new VoteUserSubject();
            vus.setSubjectId(subjectId);
            vus.setStatus(VoteConstants.VOTE_STATUS_NOT_START);
            vus.setUserId(id);
            return vus;
        }).collect(Collectors.toList());
        return voteUserSubjectMapper.batchInsertVoteUser(userList);
    }


    /**
     * 插入选项
     *
     * @param voteGuideVO 参数
     * @param subjectId 主题id
     * @return 条数
     */
    private int insertOption(VoteGuideVO voteGuideVO, Long subjectId) {
        List<OptionVO> optionList = voteGuideVO.getOptionList();
        OptionVO vo = optionList.get(0);
        if(VoteConstants.OPTION_TYPE_OPTION.equals(vo.getOptionType())) {
            List<VoteOption> voteOptionList = optionList.stream().map(optionVO -> {
                VoteOption voteOption = BeanUtil.copyProperties(optionVO, VoteOption.class);
                voteOption.setSubjectId(subjectId);
                voteOption.setDefaultOption(optionVO.getDefaultOption() ? VoteConstants.DEFAULT_OPTION_YES:
                        VoteConstants.DEFAULT_OPTION_NO);
                return voteOption;
            }).collect(Collectors.toList());
            return voteOptionMapper.batchInsertOption(voteOptionList);
        }else {
            // 分值
            VoteOption voteOption = BeanUtil.copyProperties(vo, VoteOption.class);
            voteOption.setSubjectId(subjectId);
            return voteOptionMapper.insertVoteOption(voteOption);
        }
    }

    /**
     * 插入主体
     *
     * @param voteGuideVO vo
     * @param subjectId 主题id
     * @return 主题
     */
    private int insertPrincipal(VoteGuideVO voteGuideVO, Long subjectId) {
        List<PrincipalVO> principalList = voteGuideVO.getPrincipalList();
        List<VotePrincipal> list = new ArrayList<>(principalList.size());
        for(int i = 0; i < principalList.size(); i++) {
            PrincipalVO principalVO = principalList.get(i);
            VotePrincipal votePrincipal = new VotePrincipal();
            votePrincipal.setDescription(principalVO.getDescription());
            votePrincipal.setSubjectId(subjectId);
            votePrincipal.setPrincipalOrder(i);
            list.add(votePrincipal);
        }
        return votePrincipalMapper.batchInsert(list);
    }

    /**
     * 启动投票
     *
     * @param subjectId 投票ID
     * @return 条数
     */
    @Override
    public int startSubject(Long subjectId) {

        VoteSubject subject = this.findVoteSubjectById(subjectId);
        if(subject == null) {
            throw new CustomException("投票不存在");
        }
        if(VoteConstants.STATUS_SUBJECT_NOTSTART.compareTo(subject.getStatus()) < 0) {
            throw new CustomException("只能启动未开始的投票");
        }

        List<Long> voteUsers = this.voteUsers(subjectId);
        subject.setSubjectId(subjectId);
        subject.setStatus(VoteConstants.STATUS_SUBJECT_STARTED);
        subject.setStartTime(LocalDateTime.now());
        this.sendVoteNotice(subject, voteUsers);
        return voteSubjectMapper.updateVoteSubject(subject);
    }

    /**
     * 截止投票
     *
     * @param subjectId 投票ID
     * @return 条数
     */
    @Override
    public int cutOffSubject(Long subjectId) {
        // @TODO 截止投票

        return 0;
    }

    /**
     * 开启投票任务
     *
     * @param subject 主题
     */
    public void startTask(VoteSubject subject) {
        QrtzJobDetails jobDetail = new QrtzJobDetails();
        jobDetail.setJobGroup(SystemConstants.JOB_GROUP_SYSTEM);
        jobDetail.setJobName(START_VOTE_JOB + subject.getSubjectId());
        jobDetail.setJobMethodName("startVote");
        jobDetail.setJobClassName(VoteJob.class.getName());
        jobDetail.setCronExpression(JobUtils.convertDataTimeToCron(subject.getStartTime()));
        jobDetail.setJobMethodParam(String.valueOf(subject.getSubjectId()));
        try {
            jobDetailsService.save(jobDetail);
        }catch (Exception e) {
            log.error("设置开启投票定时任务失败，", e);
        }
    }

    /**
     * 结束投票任务
     *
     * @param subject 主题
     */
    private void cutOffTask(VoteSubject subject) {
        QrtzJobDetails jobDetail = new QrtzJobDetails();
        jobDetail.setJobGroup(SystemConstants.JOB_GROUP_SYSTEM);
        jobDetail.setJobName(CUTOFF_VOTE_JOB + subject.getSubjectId());
        jobDetail.setJobMethodName("cutOffVote");
        jobDetail.setJobClassName(VoteJob.class.getName());
        jobDetail.setCronExpression(JobUtils.convertDataTimeToCron(subject.getCutOffTime()));
        jobDetail.setJobMethodParam(String.valueOf(subject.getSubjectId()));
        try {
            jobDetailsService.save(jobDetail);
        }catch (Exception e) {
            log.error("设置结束投票定时任务失败，", e);
        }
    }

    /**
     * 发送通知
     *
     * @param subject   主题
     * @param voteUsers 用户
     */
    @Override
    public void sendVoteNotice(VoteSubject subject, List<Long> voteUsers) {
        SysNotice notice = new SysNotice();
        notice.setNoticeType(SystemConstants.NOTICE_TYPE_TZ);
        notice.setUserIds(voteUsers);
        notice.setMarkedRead(SystemConstants.STATUS_NOT_READ);
        String description = subject.getDescription();
        StringBuilder sb = new StringBuilder();
        if (VoteConstants.STATUS_SUBJECT_STARTED.equals(subject.getStatus())) {
            sb.append("请参与投票");
            if (VoteConstants.REQUIRED_YES.equals(subject.getRequired())) {
                sb.append("(必填)");
            }
            sb.append(": ");
            sb.append("<a href=\"/vote/xxx\">");
            sb.append(description);
            sb.append("</a>");
            notice.setNoticeContent(sb.toString());
            notice.setNoticeTitle("投票通知");
        } else {
            sb.append("请查看投票结果");
            sb.append(": ");
            sb.append("<a href=\"/vote/xxx2\">");
            sb.append(description);
            sb.append("</a>");
            notice.setNoticeContent(sb.toString());
            notice.setNoticeTitle("投票结果查看通知");
        }
        notice.setStatus(SystemConstants.NORMAL);
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                ISysNoticeService sysNoticeService = SpringUtils.getBean(SysNoticeService.class);
                sysNoticeService.insertSysNotice(notice);
            }
        });

    }
}